//f(g())
function compose(f, g) {
    return function () {
        return f.call(this, g.apply(this, arguments));
    };
}
function add(x,y) {
    return x + y;
}
function square(x) {
    return x * x;
}

/**
 * 将类数组或对象转成数组
 * @param a
 * @param n
 */
function array(a, n) {
    Array.prototype.slice.call(a, n || 0);
}
function partialLeft(f) {
    let args = arguments;
    return function () {
        let a = array(args, 1);
        a = a.concat(array(arguments));
        return f.apply(this, a);
    };
}

function partialRight(f) {
    let args = arguments;
    return function () {
        let a = array(arguments);
        a = a.concat(array(args, 1));
        return f.apply(this, a);
    };
}

function partial(f) {
    let args = arguments;
    return function () {
        let a = array(args, 1);
        return f.apply(this, a.concat(array(arguments)));
    };
}
//memorize

function memorize(f) {
    let cache = {};
    return function () {
        let key = arguments[0];
        // let key = arguments.length + Array.prototype.join.call(arguments, ',');
        if (key in cache) {
            return cache[key];
        }
        return cache[key] = f.apply(this, arguments);
    };
}

/**
 *
 */
function jieceng(n) {
    if (n === 1) {
        return 1;
    }
    if (n === 2) {
        return 2;
    }
    return jieceng(n - 1) * jieceng(n - 2);
}

function test() {
    // console.log(compose(square, add)(4, 1))
    let t1 = new Date();
    console.log(jieceng(10));
    console.log(new Date() - t1);
    let t2 = new Date();
    console.log(memorize(jieceng)(10))
    console.log(new Date() - t2);

}

// test();
function range(from, to) {
    let r = Object.create(range.methods);
    r.from = from;
    r.to = to;
    return r;
}
range.methods = {
    includes: function (value) {
        return this.from <= value || this.to >= value;
    },
    foreach(f) {
        for (let i = Math.ceil(this.from); i < this.to; i++) {
            f(i);
        }
    },
    toString() {
        return `${this.from}...${this.to}`;
    }
}

function Range(from, to) {
    this.from = from;
    this.to = to;
}
Range.prototype = {
    constructor: Range,
    includes: function (value) {
        return this.from <= value || this.to >= value;
    },
    foreach(f) {
        for (let i = Math.ceil(this.from); i < this.to; i++) {
            f(i);
        }
    },
    toString() {
        return `${this.from}...${this.to}`;
    }
}


function test_prototype() {
    let range1 = range(2, 3);
    console.log(range1.prototype)
    window.a = range1
    window.b = new Range(2, 3);
}
// test_prototype()
function extend(o, p) {
    for (const pKey in p) {
        o[pKey] = p[pKey];
    }
    return o;
}
function defineClass(constructor, methods, staticProperties) {

    if(methods) extend(constructor.prototype, methods);
    if(staticProperties) extend(constructor, staticProperties);
    return constructor;
}

function test_defineClass() {
    window.Person = defineClass(function (name, age) {
        this.name = name;
        this.age = age;
    }, {
        firstName() {
            return this.name[0];
        }
    },{typeName: '汉族'})
}

test_defineClass();